/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.    
 */
using System;
using Token = Velocity4Net.Runtime.Parse.Token;
using System.Text;
using System.IO;
using Velocity4Net.Ctx;
using Velocity4Net.Runtime.Logging;

namespace Velocity4Net.Runtime.Parse.AST
{
    /**
     *
     */
    public class SimpleNode : INode
    {
        /** */
        protected RuntimeServices rsvc = null;

        /** */
        protected Log log = null;

        /** */
        protected INode parent;

        /** */
        protected INode[] children;

        /** */
        protected int id;

        /** */
        // TODO - It seems that this field is only valid when parsing, and should not be kept around.    
        protected Parser parser;

        /** */
        protected int info; // added

        /** */
        public bool state;

        /** */
        protected bool invalid = false;

        /** */
        protected Token first;

        /** */
        protected Token last;


        protected String templateName;


        public RuntimeServices RuntimeServices
        {
            get
            {
                return rsvc;
            }
        }

        /**
         * @param i
         */
        public SimpleNode(int i)
        {
            id = i;
        }

        /**
         * @param p
         * @param i
         */
        public SimpleNode(Parser p, int i)
            : this(i)
        {
            parser = p;
            templateName = parser.currentTemplateName;
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#jjtOpen()
         */
        public void Open()
        {
            first = parser.GetToken(1); // added
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#jjtClose()
         */
        public void Close()
        {
            last = parser.GetToken(0); // added
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#getFirstToken()
         */
        public Token FirstToken
        {
            get
            {
                return first;
            }
            set { first = value; }
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#getLastToken()
         */
        public Token LastToken
        {
            get
            {
                return last;
            }
            set { last = value; }
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#jjtSetParent(org.apache.velocity.runtime.parser.node.Node)
         */
        public void jjtSetParent(INode n)
        {
            parent = n;
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#jjtGetParent()
         */
        public INode jjtGetParent()
        {
            return parent;
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#jjtAddChild(org.apache.velocity.runtime.parser.node.Node, int)
         */
        public void jjtAddChild(INode n, int i)
        {
            if (children == null)
            {
                children = new INode[i + 1];
            }
            else if (i >= children.Length)
            {
                INode[] c = new INode[i + 1];
                Array.Copy(children, 0, c, 0, children.Length);
                children = c;
            }
            children[i] = n;
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#jjtGetChild(int)
         */
        public INode jjtGetChild(int i)
        {
            return children[i];
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#jjtGetNumChildren()
         */
        public int jjtGetNumChildren()
        {
            return (children == null) ? 0 : children.Length;
        }


        /**
         * @see org.apache.velocity.runtime.parser.node.Node#jjtAccept(org.apache.velocity.runtime.parser.node.ParserVisitor, java.lang.Object)
         */
        public virtual Object Accept(ParserVisitor visitor, Object data)
        {
            return visitor.visit(this, data);
        }


        /**
         * @see org.apache.velocity.runtime.parser.node.Node#childrenAccept(org.apache.velocity.runtime.parser.node.ParserVisitor, java.lang.Object)
         */
        public Object childrenAccept(ParserVisitor visitor, Object data)
        {
            if (children != null)
            {
                for (int i = 0; i < children.Length; ++i)
                {
                    children[i].Accept(visitor, data);
                }
            }
            return data;
        }

        /* You can override these two methods in subclasses of SimpleNode to
            customize the way the node appears when the tree is dumped.  If
            your output uses more than one line you should override
            toString(String), otherwise overriding toString() is probably all
            you need to do. */

        //    public String toString()
        // {
        //    return ParserTreeConstants.jjtNodeName[id];
        // }
        /**
         * @param prefix
         * @return String representation of this node.
         */
        public String ToString(String prefix)
        {
            return prefix + ToString();
        }

        /**
         * Override this method if you want to customize how the node dumps
         * out its children.
         *
         * @param prefix
         */
        public void dump(String prefix)
        {
            Console.WriteLine(ToString(prefix));
            if (children != null)
            {
                for (int i = 0; i < children.Length; ++i)
                {
                    SimpleNode n = (SimpleNode)children[i];
                    if (n != null)
                    {
                        n.dump(prefix + " ");
                    }
                }
            }
        }

        /**
         * Return a string that tells the current location of this node.
         */
        protected String GetLocation(InternalContextAdapter context)
        {
            return Log.formatFileString(this);
        }

        // All additional methods

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#literal()
         */
        public virtual String Literal()
        {
            // if we have only one string, just return it and avoid
            // buffer allocation. VELOCITY-606
            if (first == last)
            {
                return NodeUtils.tokenLiteral(first);
            }

            Token t = first;
            StringBuilder sb = new StringBuilder(NodeUtils.tokenLiteral(t));
            while (t != last)
            {
                t = t.next;
                sb.Append(NodeUtils.tokenLiteral(t));
            }
            return sb.ToString();
        }

        /**
         * @throws TemplateInitException 
         * @see org.apache.velocity.runtime.parser.node.Node#init(org.apache.velocity.context.InternalContextAdapter, java.lang.Object)
         */
        public virtual Object Init(InternalContextAdapter context, Object data)
        {
            /*
             * hold onto the RuntimeServices
             */

            rsvc = (RuntimeServices)data;
            log = rsvc.getLog();

            int i, k = jjtGetNumChildren();

            for (i = 0; i < k; i++)
            {
                jjtGetChild(i).Init(context, data);
            }

            return data;
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#evaluate(org.apache.velocity.context.InternalContextAdapter)
         */
        public virtual bool Evaluate(InternalContextAdapter context)
        {
            return false;
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#value(org.apache.velocity.context.InternalContextAdapter)
         */
        public virtual Object GetValue(InternalContextAdapter context)
        {
            return null;
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#render(org.apache.velocity.context.InternalContextAdapter, java.io.Writer)
         */
        public virtual bool Render(InternalContextAdapter context, TextWriter writer)
        {
            int i, k = jjtGetNumChildren();

            for (i = 0; i < k; i++)
                jjtGetChild(i).Render(context, writer);

            return true;
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#execute(java.lang.Object, org.apache.velocity.context.InternalContextAdapter)
         */
        public virtual Object Execute(Object o, InternalContextAdapter context)
        {
            return null;
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#getType()
         */
        public int NodeType
        {
            get
            {
                return id;
            }
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#getInfo()
         */
        public int Info
        {
            get
            {
                return info;
            }
            set { info = value; }
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#isInvalid()
         */
        public bool Invalid
        {
            get
            {
                return invalid;
            }
            set
            {
                invalid = value;
            }
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#getLine()
         */
        public int Line
        {
            get
            {
                return first.beginLine;
            }
        }

        /**
         * @see org.apache.velocity.runtime.parser.node.Node#getColumn()
         */
        public int Column
        {
            get
            {
                return first.beginColumn;
            }
        }

        /**
         * @since 1.5
         */
        public override String ToString()
        {
            StringBuilder tokens = new StringBuilder();

            for (Token t = FirstToken; t != null; )
            {
                tokens.Append("[").Append(t.image).Append("]");
                if (t.next != null)
                {
                    if (t.Equals(LastToken))
                    {
                        break;
                    }
                    else
                    {
                        tokens.Append(", ");
                    }
                }
                t = t.next;
            }

            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("id:{0}", NodeType);
            sb.AppendFormat("info:{0}",Info);
            sb.AppendFormat("invalid:{0}", Invalid);
            sb.AppendFormat("children:{0}", jjtGetNumChildren());
            sb.AppendFormat("tokens:{0}", tokens);
            return sb.ToString();
        }

        public String TemplateName
        {
            get
            {
                return templateName;
            }
        }
    }

}
